package com.zhny.aiot.dfst.ecois.soil.plugin;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhny.aiot.dfst.ecois.soil.plugin.model.EcoisSoilStoreEntry;
import com.zhny.aiot.dfst.ecois.soil.plugin.result.EcoisHttpResult;
import com.zhny.aiot.dfst.ecois.soil.plugin.result.EcoisToken;
import com.zhny.aiot.dfst.ecois.soil.plugin.result.EcoisValue;
import com.zhny.aiot.plugin.ICenterFacilities;
import com.zhny.aiot.plugin.IDeviceFactory;
import com.zhny.aiot.plugin.core.utils.MessageUtils;
import com.zhny.aiot.plugin.model.DeviceEntity;
import com.zhny.aiot.plugin.model.MessageEntry;
import com.zhny.aiot.plugin.model.MessageType;
import com.zhny.aiot.plugin.model.TaskEntity;
import com.zhny.aiot.plugin.web.WebDevice;
import io.netty.util.internal.StringUtil;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;

public class EcoisSoilDevice extends WebDevice<EcoisSoilConfig> {
    private static final Logger log = LoggerFactory.getLogger(EcoisSoilDevice.class);
    private EcoisToken token;
    public EcoisSoilDevice(IDeviceFactory<EcoisSoilConfig> factory, ICenterFacilities centerFacilities, DeviceEntity entity) {
        super (factory, centerFacilities, entity);
    }

    @Override
    protected void onRequestHttp(TaskEntity taskEntity, CloseableHttpClient closeableHttpClient) {
        if(isTokenExpires ()){
            token = onTokenHttp (closeableHttpClient);
        }
        successResponse(closeableHttpClient);
    }

    private boolean isTokenExpires(){
        if(token == null)
            return true;
        Long oldTimeStamp = token.getTime ();
        Long currentTimeStamp = System.currentTimeMillis ();
        Long expires = token.getExpires ();
        if(((currentTimeStamp - oldTimeStamp)/1000)>= expires)
            return true;
        return false;
    }

    private EcoisToken onTokenHttp(CloseableHttpClient httpClient){
        log.info ("execute ecois soil api token request!");
        URIBuilder uriBuilder =null;
        EcoisSoilConfig config =this.getConfig ();
        try {
            uriBuilder=new URIBuilder ("http://openapi.ecois.info/v3/token");
            uriBuilder.addParameter("appid", config.getAppid ());
            uriBuilder.addParameter("secret", config.getSecret ());
            HttpGet httpGet = new HttpGet (uriBuilder.build ());
            try (CloseableHttpResponse response =httpClient.execute (httpGet) ){
                String body =EntityUtils.toString (response.getEntity ());
                EcoisToken newToken = mapTo(body,EcoisToken.class);
                if(newToken == null)
                    throw new RuntimeException ("ecois token api result map to error !");
                newToken.setTime (System.currentTimeMillis ());
                return newToken;
            } catch (ClientProtocolException e) {
                throw new RuntimeException (e);
            } catch (ConnectTimeoutException e) {
                throw new RuntimeException("请求连接超时");
            } catch (IOException e) {
                throw new RuntimeException (e);
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException (e);
        }

    }

    private void successResponse(CloseableHttpClient httpClient) {
        log.info ("execute ecois soil api data request!");
        URIBuilder uriBuilder =null;
        EcoisSoilConfig config =this.getConfig ();
        try {
            uriBuilder=new URIBuilder ("http://openapi.ecois.info/v3/device/"+config.getSn ()+"/latest");
            HttpGet httpGet=new HttpGet (uriBuilder.build ());
            httpGet.addHeader ("Authorization",this.token.getToken ());
            try (CloseableHttpResponse response =httpClient.execute (httpGet) ){
                String body =EntityUtils.toString (response.getEntity ());
                EcoisHttpResult result = mapTo(body,EcoisHttpResult.class);
                if(result != null){
                    MessageEntry messageEntry = MessageUtils.createMessageEntry(this.getId ());
                    messageEntry.setType (MessageType.RESULT);
                    if(result.getValues ().size () == 0)
                        return;
                    EcoisSoilStoreEntry entry = getStoreEntry(result);
                    messageEntry.setData (entry);
                    nofity (messageEntry);
                    save (entry);
                    saveReal (entry);
                }

            } catch (ClientProtocolException e) {
                throw new RuntimeException (e);
            } catch (ConnectTimeoutException e) {
                throw new RuntimeException("请求连接超时");
            } catch (IOException e) {
                throw new RuntimeException (e);
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException (e);
        }
    }

    private   <T> T mapTo(String body,Class<T> cls){
        if(StringUtil.isNullOrEmpty (body))
            return null;
        ObjectMapper mapper=new ObjectMapper ();
        try {
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            T obj=mapper.readValue (body,cls);
            return obj;
        } catch (IOException e) {
            return null;
        }
    }

    private EcoisSoilStoreEntry getStoreEntry(EcoisHttpResult result){
        EcoisValue value = result.getValues ().get ("地表");
        EcoisValue value_10 = result.getValues ().get ("10cm");
        EcoisValue value_20 = result.getValues ().get ("20cm");
        EcoisValue value_30 = result.getValues ().get ("30cm");
        EcoisValue value_40 = result.getValues ().get ("40cm");
        EcoisValue value_50 = result.getValues ().get ("50cm");
        EcoisValue value_60 = result.getValues ().get ("60cm");
        EcoisValue value_70 = result.getValues ().get ("70cm");
        EcoisValue value_80 = result.getValues ().get ("80cm");
        EcoisValue value_90 = result.getValues ().get ("90cm");
        EcoisValue value_100 = result.getValues ().get ("100cm");
        EcoisSoilStoreEntry entry = new EcoisSoilStoreEntry ();
        entry.setTemperature (value != null?value.getTemperature ():null);
        entry.setMoisture10cm (value_10 != null?value_10.getMoisture ():null);
        entry.setTemperature10cm (value_10 != null?value_10.getTemperature ():null);
        entry.setMoisture20cm (value_20 != null?value_20.getMoisture ():null);
        entry.setTemperature20cm (value_20 != null?value_20.getTemperature ():null);
        entry.setMoisture30cm (value_30 != null?value_30.getMoisture ():null);
        entry.setTemperature30cm (value_30 != null?value_30.getTemperature ():null);
        entry.setMoisture40cm (value_40 != null?value_40.getMoisture ():null);
        entry.setTemperature40cm (value_40 != null?value_40.getTemperature ():null);
        entry.setMoisture50cm (value_50 != null?value_50.getMoisture ():null);
        entry.setTemperature50cm (value_50 != null?value_50.getTemperature ():null);
        entry.setMoisture60cm (value_60 != null?value_60.getMoisture ():null);
        entry.setTemperature60cm (value_60 != null?value_60.getTemperature ():null);
        entry.setMoisture70cm (value_70 != null?value_70.getMoisture ():null);
        entry.setTemperature70cm (value_70 != null?value_70.getTemperature ():null);
        entry.setMoisture80cm (value_80 != null?value_80.getMoisture ():null);
        entry.setTemperature80cm (value_80 != null?value_80.getTemperature ():null);
        entry.setMoisture90cm (value_90 != null?value_90.getMoisture ():null);
        entry.setTemperature90cm (value_90 != null?value_90.getTemperature ():null);
        entry.setTemperature100cm (value_100 != null?value_100.getTemperature ():null);
        entry.setMoisture100cm (value_100 != null?value_100.getMoisture ():null);
        return entry;
    }
}
